{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Max Pooling\n",
    "\n",
    "### 前向过程\n",
    "\n",
    "参考[池化层的反向传播](0_2_5-池化层的反向传播-MaxPooling、AveragePooling、GlobalAveragePooling.md)中公式(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "def max_pooling_forward(z, pooling, strides=(2, 2), padding=(0, 0)):\n",
    "    \"\"\"\n",
    "    最大池化前向过程\n",
    "    :param z: 卷积层矩阵,形状(N,C,H,W)，N为batch_size，C为通道数\n",
    "    :param pooling: 池化大小(k1,k2)\n",
    "    :param strides: 步长\n",
    "    :param padding: 0填充\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    N, C, H, W = z.shape\n",
    "    # 零填充\n",
    "    padding_z = np.lib.pad(z, ((0, 0), (0, 0), (padding[0], padding[0]), (padding[1], padding[1])), 'constant', constant_values=0)\n",
    "\n",
    "    # 输出的高度和宽度\n",
    "    out_h = (H + 2 * padding[0] - pooling[0]) // strides[0] + 1\n",
    "    out_w = (W + 2 * padding[1] - pooling[1]) // strides[1] + 1\n",
    "\n",
    "    pool_z = np.zeros((N, C, out_h, out_w))\n",
    "\n",
    "    for n in np.arange(N):\n",
    "        for c in np.arange(C):\n",
    "            for i in np.arange(out_h):\n",
    "                for j in np.arange(out_w):\n",
    "                    pool_z[n, c, i, j] = np.max(padding_z[n, c,\n",
    "                                                          strides[0] * i:strides[0] * i + pooling[0],\n",
    "                                                          strides[1] * j:strides[1] * j + pooling[1]])\n",
    "    return pool_z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 反向过程\n",
    "\n",
    "参考[池化层的反向传播](0_2_5-池化层的反向传播-MaxPooling、AveragePooling、GlobalAveragePooling.md)中公式(6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def max_pooling_backward(next_dz, z, pooling, strides=(2, 2), padding=(0, 0)):\n",
    "    \"\"\"\n",
    "    最大池化反向过程\n",
    "    :param next_dz：损失函数关于最大池化输出的损失\n",
    "    :param z: 卷积层矩阵,形状(N,C,H,W)，N为batch_size，C为通道数\n",
    "    :param pooling: 池化大小(k1,k2)\n",
    "    :param strides: 步长\n",
    "    :param padding: 0填充\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    N, C, H, W = z.shape\n",
    "    _, _, out_h, out_w = next_dz.shape\n",
    "    # 零填充\n",
    "    padding_z = np.lib.pad(z, ((0, 0), (0, 0), (padding[0], padding[0]), (padding[1], padding[1])), 'constant',\n",
    "                           constant_values=0)\n",
    "    # 零填充后的梯度\n",
    "    padding_dz = np.zeros_like(padding_z)\n",
    "\n",
    "    for n in np.arange(N):\n",
    "        for c in np.arange(C):\n",
    "            for i in np.arange(out_h):\n",
    "                for j in np.arange(out_w):\n",
    "                    # 找到最大值的那个元素坐标，将梯度传给这个坐标\n",
    "                    flat_idx = np.argmax(padding_z[n, c,\n",
    "                                                   strides[0] * i:strides[0] * i + pooling[0],\n",
    "                                                   strides[1] * j:strides[1] * j + pooling[1]])\n",
    "                    h_idx = strides[0] * i + flat_idx // pooling[1]\n",
    "                    w_idx = strides[1] * j + flat_idx % pooling[1]\n",
    "                    padding_dz[n, c, h_idx, w_idx] += next_dz[n, c, i, j]\n",
    "    # 返回时剔除零填充\n",
    "    return _remove_padding(padding_dz, padding) \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Average Pooling\n",
    "\n",
    "### 前向过程\n",
    "\n",
    "参考[池化层的反向传播](0_2_5-池化层的反向传播-MaxPooling、AveragePooling、GlobalAveragePooling.md)中公式(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def avg_pooling_forward(z, pooling, strides=(2, 2), padding=(0, 0)):\n",
    "    \"\"\"\n",
    "    平均池化前向过程\n",
    "    :param z: 卷积层矩阵,形状(N,C,H,W)，N为batch_size，C为通道数\n",
    "    :param pooling: 池化大小(k1,k2)\n",
    "    :param strides: 步长\n",
    "    :param padding: 0填充\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    N, C, H, W = z.shape\n",
    "    # 零填充\n",
    "    padding_z = np.lib.pad(z, ((0, 0), (0, 0), (padding[0], padding[0]), (padding[1], padding[1])), 'constant',\n",
    "                           constant_values=0)\n",
    "\n",
    "    # 输出的高度和宽度\n",
    "    out_h = (H + 2 * padding[0] - pooling[0]) // strides[0] + 1\n",
    "    out_w = (W + 2 * padding[1] - pooling[1]) // strides[1] + 1\n",
    "\n",
    "    pool_z = np.zeros((N, C, out_h, out_w))\n",
    "\n",
    "    for n in np.arange(N):\n",
    "        for c in np.arange(C):\n",
    "            for i in np.arange(out_h):\n",
    "                for j in np.arange(out_w):\n",
    "                    pool_z[n, c, i, j] = np.mean(padding_z[n, c,\n",
    "                                                           strides[0] * i:strides[0] * i + pooling[0],\n",
    "                                                           strides[1] * j:strides[1] * j + pooling[1]])\n",
    "    return pool_z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 反向过程\n",
    "\n",
    "参考[池化层的反向传播](0_2_5-池化层的反向传播-MaxPooling、AveragePooling、GlobalAveragePooling.md)中公式(9)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def avg_pooling_backward(next_dz, z, pooling, strides=(2, 2), padding=(0, 0)):\n",
    "    \"\"\"\n",
    "    平均池化反向过程\n",
    "    :param next_dz：损失函数关于最大池化输出的损失\n",
    "    :param z: 卷积层矩阵,形状(N,C,H,W)，N为batch_size，C为通道数\n",
    "    :param pooling: 池化大小(k1,k2)\n",
    "    :param strides: 步长\n",
    "    :param padding: 0填充\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    N, C, H, W = z.shape\n",
    "    _, _, out_h, out_w = next_dz.shape\n",
    "    # 零填充\n",
    "    padding_z = np.lib.pad(z, ((0, 0), (0, 0), (padding[0], padding[0]), (padding[1], padding[1])), 'constant',\n",
    "                           constant_values=0)\n",
    "    # 零填充后的梯度\n",
    "    padding_dz = np.zeros_like(padding_z)\n",
    "\n",
    "    for n in np.arange(N):\n",
    "        for c in np.arange(C):\n",
    "            for i in np.arange(out_h):\n",
    "                for j in np.arange(out_w):\n",
    "                    # 每个神经元均分梯度\n",
    "                    padding_dz[n, c,\n",
    "                               strides[0] * i:strides[0] * i + pooling[0],\n",
    "                               strides[1] * j:strides[1] * j + pooling[1]] += next_dz[n, c, i, j] / (pooling[0] * pooling[1])\n",
    "    # 返回时剔除零填充\n",
    "    return _remove_padding(padding_dz, padding)  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Global Max Pooling\n",
    "\n",
    "### 前向过程\n",
    "\n",
    "参考[池化层的反向传播](0_2_5-池化层的反向传播-MaxPooling、AveragePooling、GlobalAveragePooling.md)中公式(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def global_max_pooling_forward(z):\n",
    "    \"\"\"\n",
    "    全局最大池化前向过程\n",
    "    :param z: 卷积层矩阵,形状(N,C,H,W)，N为batch_size，C为通道数\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    return np.max(np.max(z, axis=-1), -1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 反向过程\n",
    "\n",
    "参考[池化层的反向传播](0_2_5-池化层的反向传播-MaxPooling、AveragePooling、GlobalAveragePooling.md)中公式(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def global_max_pooling_forward(next_dz, z):\n",
    "    \"\"\"\n",
    "    全局最大池化反向过程\n",
    "    :param next_dz: 全局最大池化梯度，形状(N,C)\n",
    "    :param z: 卷积层矩阵,形状(N,C,H,W)，N为batch_size，C为通道数\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    N, C, H, W = z.shape\n",
    "    dz = np.zeros_like(z)\n",
    "    for n in np.arange(N):\n",
    "        for c in np.arange(C):\n",
    "            # 找到最大值所在坐标，梯度传给这个坐标\n",
    "            idx = np.argmax(z[n, c, :, :])\n",
    "            h_idx = idx // W\n",
    "            w_idx = idx % W\n",
    "            dz[n, c, h_idx, w_idx] = next_dz[n, c]\n",
    "    return dz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Global Average Pooling\n",
    "\n",
    "### 前向过程\n",
    "\n",
    "参考[池化层的反向传播](0_2_5-池化层的反向传播-MaxPooling、AveragePooling、GlobalAveragePooling.md)中公式(4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def global_avg_pooling_forward(z):\n",
    "    \"\"\"\n",
    "    全局平均池化前向过程\n",
    "    :param z: 卷积层矩阵,形状(N,C,H,W)，N为batch_size，C为通道数\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    return np.mean(np.mean(z, axis=-1), axis=-1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 反向过程\n",
    "\n",
    "参考[池化层的反向传播](0_2_5-池化层的反向传播-MaxPooling、AveragePooling、GlobalAveragePooling.md)中公式(12)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def global_avg_pooling_backward(next_dz, z):\n",
    "    \"\"\"\n",
    "    全局平均池化反向过程\n",
    "    :param next_dz: 全局最大池化梯度，形状(N,C)\n",
    "    :param z: 卷积层矩阵,形状(N,C,H,W)，N为batch_size，C为通道数\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    N, C, H, W = z.shape\n",
    "    dz = np.zeros_like(z)\n",
    "    for n in np.arange(N):\n",
    "        for c in np.arange(C):\n",
    "            # 梯度平分给相关神经元\n",
    "            dz[n, c, :, :] += next_dz[n, c] / (H * W)\n",
    "    return dz"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cython加速\n",
    "\n",
    "对于最大池化层的前向和后向过程使用Cython编译加速,实际测试发现耗时减少约20%,貌似提升效果不大;对Cython使用不精通，哪位大佬知道如何改进，请不吝赐教，感谢！！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The Cython extension is already loaded. To reload it, use:\n",
      "  %reload_ext Cython\n"
     ]
    }
   ],
   "source": [
    "%load_ext Cython"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n",
      "Error compiling Cython file:\n",
      "------------------------------------------------------------\n",
      "...\n",
      "    cdef unsigned int N = z.shape[0]\n",
      "    cdef unsigned int C = z.shape[1]\n",
      "    cdef unsigned int H = z.shape[2]\n",
      "    cdef unsigned int W = z.shape[3]\n",
      "    # 零填充\n",
      "    cdef np.ndarray[double, ndim= 4] padding_z = np.lib.pad(z, ((0, 0), (0, 0),\n",
      "                                                  ^\n",
      "------------------------------------------------------------\n",
      "\n",
      "C:\\Users\\mick.yi\\.ipython\\cython\\_cython_magic_084155bbdaf3d31108ec7122b8d3e0b8.pyx:20:51: cimported module has no attribute 'lib'\n",
      "\n",
      "Error compiling Cython file:\n",
      "------------------------------------------------------------\n",
      "...\n",
      "\n",
      "    # 输出的高度和宽度\n",
      "    cdef unsigned int out_h = (H + 2 * padding[0] - pooling[0]) // strides[0] + 1\n",
      "    cdef unsigned int out_w = (W + 2 * padding[1] - pooling[1]) // strides[1] + 1\n",
      "\n",
      "    cdef np.ndarray[double, ndim= 4] pool_z = np.zeros((N, C, out_h, out_w)).astype(np.float64)\n",
      "                                               ^\n",
      "------------------------------------------------------------\n",
      "\n",
      "C:\\Users\\mick.yi\\.ipython\\cython\\_cython_magic_084155bbdaf3d31108ec7122b8d3e0b8.pyx:28:48: cimported module has no attribute 'zeros'\n",
      "\n",
      "Error compiling Cython file:\n",
      "------------------------------------------------------------\n",
      "...\n",
      "\n",
      "    # 输出的高度和宽度\n",
      "    cdef unsigned int out_h = (H + 2 * padding[0] - pooling[0]) // strides[0] + 1\n",
      "    cdef unsigned int out_w = (W + 2 * padding[1] - pooling[1]) // strides[1] + 1\n",
      "\n",
      "    cdef np.ndarray[double, ndim= 4] pool_z = np.zeros((N, C, out_h, out_w)).astype(np.float64)\n",
      "                                                                                     ^\n",
      "------------------------------------------------------------\n",
      "\n",
      "C:\\Users\\mick.yi\\.ipython\\cython\\_cython_magic_084155bbdaf3d31108ec7122b8d3e0b8.pyx:28:86: cimported module has no attribute 'float64'\n",
      "\n",
      "Error compiling Cython file:\n",
      "------------------------------------------------------------\n",
      "...\n",
      "    cdef unsigned int out_w = (W + 2 * padding[1] - pooling[1]) // strides[1] + 1\n",
      "\n",
      "    cdef np.ndarray[double, ndim= 4] pool_z = np.zeros((N, C, out_h, out_w)).astype(np.float64)\n",
      "\n",
      "    cdef unsigned int n, c, i, j\n",
      "    for n in np.arange(N):\n",
      "              ^\n",
      "------------------------------------------------------------\n",
      "\n",
      "C:\\Users\\mick.yi\\.ipython\\cython\\_cython_magic_084155bbdaf3d31108ec7122b8d3e0b8.pyx:31:15: cimported module has no attribute 'arange'\n",
      "\n",
      "Error compiling Cython file:\n",
      "------------------------------------------------------------\n",
      "...\n",
      "\n",
      "    cdef np.ndarray[double, ndim= 4] pool_z = np.zeros((N, C, out_h, out_w)).astype(np.float64)\n",
      "\n",
      "    cdef unsigned int n, c, i, j\n",
      "    for n in np.arange(N):\n",
      "        for c in np.arange(C):\n",
      "                  ^\n",
      "------------------------------------------------------------\n",
      "\n",
      "C:\\Users\\mick.yi\\.ipython\\cython\\_cython_magic_084155bbdaf3d31108ec7122b8d3e0b8.pyx:32:19: cimported module has no attribute 'arange'\n",
      "\n",
      "Error compiling Cython file:\n",
      "------------------------------------------------------------\n",
      "...\n",
      "    cdef np.ndarray[double, ndim= 4] pool_z = np.zeros((N, C, out_h, out_w)).astype(np.float64)\n",
      "\n",
      "    cdef unsigned int n, c, i, j\n",
      "    for n in np.arange(N):\n",
      "        for c in np.arange(C):\n",
      "            for i in np.arange(out_h):\n",
      "                      ^\n",
      "------------------------------------------------------------\n",
      "\n",
      "C:\\Users\\mick.yi\\.ipython\\cython\\_cython_magic_084155bbdaf3d31108ec7122b8d3e0b8.pyx:33:23: cimported module has no attribute 'arange'\n",
      "\n",
      "Error compiling Cython file:\n",
      "------------------------------------------------------------\n",
      "...\n",
      "\n",
      "    cdef unsigned int n, c, i, j\n",
      "    for n in np.arange(N):\n",
      "        for c in np.arange(C):\n",
      "            for i in np.arange(out_h):\n",
      "                for j in np.arange(out_w):\n",
      "                          ^\n",
      "------------------------------------------------------------\n",
      "\n",
      "C:\\Users\\mick.yi\\.ipython\\cython\\_cython_magic_084155bbdaf3d31108ec7122b8d3e0b8.pyx:34:27: cimported module has no attribute 'arange'\n",
      "\n",
      "Error compiling Cython file:\n",
      "------------------------------------------------------------\n",
      "...\n",
      "    cdef unsigned int n, c, i, j\n",
      "    for n in np.arange(N):\n",
      "        for c in np.arange(C):\n",
      "            for i in np.arange(out_h):\n",
      "                for j in np.arange(out_w):\n",
      "                    pool_z[n, c, i, j] = np.max(padding_z[n, c,\n",
      "                                          ^\n",
      "------------------------------------------------------------\n",
      "\n",
      "C:\\Users\\mick.yi\\.ipython\\cython\\_cython_magic_084155bbdaf3d31108ec7122b8d3e0b8.pyx:35:43: cimported module has no attribute 'max'\n"
     ]
    }
   ],
   "source": [
    "%%cython\n",
    "cimport cython\n",
    "cimport numpy as np\n",
    "cpdef max_pooling_forward(np.ndarray[double, ndim=4] z,\n",
    "                        tuple pooling,\n",
    "                        tuple strides=(2, 2),\n",
    "                        tuple padding=(0, 0)):\n",
    "    \"\"\"\n",
    "    最大池化前向过程\n",
    "    :param z: 卷积层矩阵,形状(N,C,H,W)，N为batch_size，C为通道数\n",
    "    :param pooling: 池化大小(k1,k2)\n",
    "    :param strides: 步长\n",
    "    :param padding: 0填充\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    cdef unsigned int N = z.shape[0]\n",
    "    cdef unsigned int C = z.shape[1]\n",
    "    cdef unsigned int H = z.shape[2]\n",
    "    cdef unsigned int W = z.shape[3]\n",
    "    # 零填充\n",
    "    cdef np.ndarray[double, ndim= 4] padding_z = np.lib.pad(z, ((0, 0), (0, 0),\n",
    "                                                                 (padding[0], padding[0]), (padding[1], padding[1])),\n",
    "                                                             'constant', constant_values=0)\n",
    "\n",
    "    # 输出的高度和宽度\n",
    "    cdef unsigned int out_h = (H + 2 * padding[0] - pooling[0]) // strides[0] + 1\n",
    "    cdef unsigned int out_w = (W + 2 * padding[1] - pooling[1]) // strides[1] + 1\n",
    "\n",
    "    cdef np.ndarray[double, ndim= 4] pool_z = np.zeros((N, C, out_h, out_w)).astype(np.float64)\n",
    "\n",
    "    cdef unsigned int n, c, i, j\n",
    "    for n in np.arange(N):\n",
    "        for c in np.arange(C):\n",
    "            for i in np.arange(out_h):\n",
    "                for j in np.arange(out_w):\n",
    "                    pool_z[n, c, i, j] = np.max(padding_z[n, c,\n",
    "                                                          strides[0] * i:strides[0] * i + pooling[0],\n",
    "                                                          strides[1] * j:strides[1] * j + pooling[1]])\n",
    "    return pool_z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n",
      "Error compiling Cython file:\n",
      "------------------------------------------------------------\n",
      "...\n",
      "    for n in np.arange(N):\n",
      "        for c in np.arange(C):\n",
      "            for i in np.arange(out_h):\n",
      "                for j in np.arange(out_w):\n",
      "                    # 找到最大值的那个元素坐标，将梯度传给这个坐标\n",
      "                    flat_idx = np.argmax(padding_z[n, c,\n",
      "                                ^\n",
      "------------------------------------------------------------\n",
      "\n",
      "C:\\Users\\mick.yi\\.ipython\\cython\\_cython_magic_8bb6084ae7126dc2f2ac19274f21dbc7.pyx:37:33: cimported module has no attribute 'argmax'\n",
      "\n",
      "Error compiling Cython file:\n",
      "------------------------------------------------------------\n",
      "...\n",
      "    for n in np.arange(N):\n",
      "        for c in np.arange(C):\n",
      "            for i in np.arange(out_h):\n",
      "                for j in np.arange(out_w):\n",
      "                    # 找到最大值的那个元素坐标，将梯度传给这个坐标\n",
      "                    flat_idx = np.argmax(padding_z[n, c,\n",
      "                                ^\n",
      "------------------------------------------------------------\n",
      "\n",
      "C:\\Users\\mick.yi\\.ipython\\cython\\_cython_magic_8bb6084ae7126dc2f2ac19274f21dbc7.pyx:37:33: Compiler crash in AnalyseExpressionsTransform\n",
      "\n",
      "ModuleNode.body = StatListNode(_cython_magic_8bb6084ae7126dc2f2ac19274f21dbc7.pyx:1:0)\n",
      "StatListNode.stats[2] = StatListNode(_cython_magic_8bb6084ae7126dc2f2ac19274f21dbc7.pyx:3:6)\n",
      "StatListNode.stats[0] = CFuncDefNode(_cython_magic_8bb6084ae7126dc2f2ac19274f21dbc7.pyx:3:6,\n",
      "    args = [...]/5,\n",
      "    doc = '\\n    最大池化反向过程\\n    :param next_dz：损失函数关于最大池化输出的损失\\n    :param z: 卷积层矩阵,形状(N,C,H,W)，N为batch_size，C为通道数\\n    :param pooling: 池化大小(k1,k2)\\n    :param strides: 步长\\n    :param padding: 0填充\\n    :return:\\n    ',\n",
      "    modifiers = [...]/0,\n",
      "    overridable = 1,\n",
      "    visibility = 'private')\n",
      "File 'ExprNodes.py', line 5084, in infer_type: SimpleCallNode(_cython_magic_8bb6084ae7126dc2f2ac19274f21dbc7.pyx:37:40,\n",
      "    result_is_used = True,\n",
      "    use_managed_ref = True)\n",
      "File 'ExprNodes.py', line 6481, in infer_type: AttributeNode(_cython_magic_8bb6084ae7126dc2f2ac19274f21dbc7.pyx:37:33,\n",
      "    attribute = 'argmax',\n",
      "    is_attribute = 1,\n",
      "    is_numpy_attribute = True,\n",
      "    needs_none_check = True,\n",
      "    result_is_used = True,\n",
      "    use_managed_ref = True)\n",
      "\n",
      "Compiler crash traceback from this point on:\n",
      "  File \"d:\\anaconda3\\lib\\site-packages\\Cython\\Compiler\\ExprNodes.py\", line 6481, in infer_type\n",
      "    return node.entry.type\n",
      "AttributeError: 'NoneType' object has no attribute 'type'\n"
     ]
    }
   ],
   "source": [
    "%%cython\n",
    "cimport cython\n",
    "cimport numpy as np\n",
    "cpdef max_pooling_backward(np.ndarray[double, ndim=4] next_dz,\n",
    "                         np.ndarray[double, ndim=4] z,\n",
    "                         tuple pooling,\n",
    "                         tuple strides=(2, 2),\n",
    "                         tuple padding=(0, 0)):\n",
    "    \"\"\"\n",
    "    最大池化反向过程\n",
    "    :param next_dz：损失函数关于最大池化输出的损失\n",
    "    :param z: 卷积层矩阵,形状(N,C,H,W)，N为batch_size，C为通道数\n",
    "    :param pooling: 池化大小(k1,k2)\n",
    "    :param strides: 步长\n",
    "    :param padding: 0填充\n",
    "    :return:\n",
    "    \"\"\"\n",
    "    cdef unsigned int N = z.shape[0]\n",
    "    cdef unsigned int C = z.shape[1]\n",
    "    cdef unsigned int H = z.shape[2]\n",
    "    cdef unsigned int W = z.shape[3]\n",
    "    cdef unsigned int out_h = next_dz.shape[2]\n",
    "    cdef unsigned int out_w = next_dz.shape[3]\n",
    "    # 零填充\n",
    "    cdef np.ndarray[double, ndim = 4] padding_z = np.lib.pad(z, ((0, 0), (0, 0),\n",
    "                                                                (padding[0], padding[0]),\n",
    "                                                                (padding[1], padding[1])),\n",
    "                                                            'constant', constant_values=0)\n",
    "    # 零填充后的梯度\n",
    "    cdef np.ndarray[double, ndim = 4] padding_dz = np.zeros_like(padding_z).astype(np.float64)\n",
    "\n",
    "    cdef unsigned int n, c, i, j\n",
    "    for n in np.arange(N):\n",
    "        for c in np.arange(C):\n",
    "            for i in np.arange(out_h):\n",
    "                for j in np.arange(out_w):\n",
    "                    # 找到最大值的那个元素坐标，将梯度传给这个坐标\n",
    "                    flat_idx = np.argmax(padding_z[n, c,\n",
    "                                                   strides[0] * i:strides[0] * i + pooling[0],\n",
    "                                                   strides[1] * j:strides[1] * j + pooling[1]])\n",
    "                    h_idx = strides[0] * i + flat_idx // pooling[1]\n",
    "                    w_idx = strides[1] * j + flat_idx % pooling[1]\n",
    "                    padding_dz[n, c, h_idx, w_idx] += next_dz[n, c, i, j]\n",
    "    # 返回时剔除零填充\n",
    "    return _remove_padding(padding_dz, padding)  # padding_z[:, :, padding[0]:-padding[0], padding[1]:-padding[1]]"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
